Le nouveau rideau de fer

Un exemple de carte en 2.5D

Nicolas Lambert (RIATE, CNRS)

featured

Introduction

Ce document montre comment réaliser cette carte de discontuités en 2,5D (fausse 3D) joliement mise en page entrièrement dans le langage R. Des versions antérieures de cette carte ont déjà été publiées par le passé, dans le manuel de cartographie (Lambert, Zanin 2016), ou dans Mad Maps (Lambert, Zanin 2019) et ont fait l’objet de billets de blog (Lambert 2017). Ici, nous entendons prouver qu’il est possible de réaliser ce type de carte sans passer par un logiciel DAO (Lambert 2019). Les sources et les références sont précisées à la fin du document.

1 Préparation des données

1.1 Packages

Pour réaliser cette carte, nous nous nous appuyons sur 5 packages : le package sf (Pebesma 2018) pour gérer les objets spatiaux, le package mapsf (Giraud 2021) pour l’affichage des différentes couches composant la carte, le package eurostat (Lahti et al. 2017) pour les données et le package rnaturalEarth (South 2017) pour quelques couches d’habillages supplémentaires. Le package scales (Wickham, Seidel 2020) sera uniquement utilisé pour réaliser une opération de rééchelonnage d’une série statistique.

library("sf")
library("mapsf")
library("eurostat")
library("rnaturalearth")
library("scales")

1.2 Données géométriques

Avec le package eurostat, nous créons d’un fond de carte hybride avec maillage homogène NUTS2/3. Tous les pays sont au niveau NUTS3 version 2016 sauf l’Autriche, la Belgique, la Suisse, l’Allemagne, la Grèce, les Pays-Bas, la Turquie, l’Irlande, l’Islande et la Norvège. Pour des raisons de disponibilité des données post Brexit, Le Royaume-Uni est au niveau NUTS2 version 2013.

Récupération des découpages NUTS de 2016 et construction d’un fond de carte NUTS 2/3 :

# NUTS 2016
nuts2016 <- get_eurostat_geospatial(
            output_class = "sf",
            resolution = "20",
            nuts_level = "all",
            year = "2016")

# Couche géographique des NUTS3 et NUTS2 pour 2016
nuts2016_3 <- nuts2016[nuts2016$LEVL_CODE == 3, ]
nuts2016_2 <- nuts2016[nuts2016$LEVL_CODE == 2, ]

# Liste des pays (code ISO) pour lesquels on s'intéresse aux NUTS2 
N2 <- c("AT", "BE", "CH", "DE", "EL", "NL", "UK", "TR", "IE", "IS", "NO")

# Couche géographique NUTS2/3 en fonction de la liste de pays N2
nuts <- rbind(nuts2016_2[nuts2016_2$CNTR_CODE %in% N2, ],
              nuts2016_3[!nuts2016_3$CNTR_CODE %in% N2, ])

# Suppression des départements d'Outre-mer français
nuts <- nuts[!nuts$id %in% c("FRY10", "FRY20", "FRY30", "FRY40", "FRY50"), ]

# Suppression du découpage NUTS 2016 pour le Royaume-Uni 
nuts <- nuts[nuts$CNTR_CODE != "UK", ]

# Sélection et renommage de colonnes
nuts <- nuts[,c("id","NUTS_NAME","geometry")]
colnames(nuts) <- c("id","name","geometry")

Fusion avec le découpage NUTS 2 2013 du Royaume-Uni :

# Récupération du découpage NUTS2 2013
nuts2013 <- get_eurostat_geospatial(
            output_class = "sf",
            resolution = "20",
            nuts_level = "2",
            year = "2013")

# Sélection des NUTS2 du Royaume-Uni et renommage de colonnes
uk <- nuts2013[nuts2013$CNTR_CODE == "UK",]

# Sélection et renommage de colonnes
uk <- uk[,c("id","NUTS_NAME","geometry")]
colnames(uk) <- c("id","name","geometry")

# Fusion des NUTS2 2013 du Royaume-Uni avec le fond de carte NUTS2/3 2016
nuts <- rbind(nuts, uk)

La couche géographique (objet sf) créée est donc une fusion des découpages territoriaux suivants :

1.3 Données statistiques

Grâce au package eurostat, nous récupérons les données de PIB par habitant en 2016.

# Récupération des données (PIB/hab)
var <- "nama_10r_3gdp"
gdpinh <- get_eurostat(var, time_format = "num")

# Sélection de l'unité de mesure et de la date
gdpinh <- gdpinh[gdpinh$unit == "EUR_HAB",]
gdpinh <- gdpinh[gdpinh$time == 2016, c("geo","values")]
colnames(gdpinh) <- c("id","GDPINH_2016")

Les données pour le Royaume-Uni ne sont plus disponibles depuis le Brexit. Les données sont également manquantes pour quelques unités territoriales. Nous combinons donc les données issues d’eurostat avec des estimations issues de la base de données ESPON. Celles-si ont été préalablement stockées dans dans le fichier missing.csv mis à disposition (cf. ci-dessous).




Télécharger les données


Nous combinons les deux jeux de données pour avoir une couverture exhaustive de l’espace à représenter :

missing <- read.csv("data/missing.csv")
gdpinh <- rbind(gdpinh, missing)

Pour des questions de reproductibilité, nous sauvegardons les données ainsi complétées dans le répertoire local data.

write.csv(gdpinh, "data/gdpinh.csv")

Nous effectuons ensuite une jointure entre les données et les géométries.

nuts <- merge(
        x = nuts,
        y = gdpinh,
        by = "id",
        all.x = TRUE)

2 Modèle de mise en page

2.1 Couches d’habillage

Pour compléter la carte, nous utilisons des couches géographiques proposées par le package rnaturalearth.

land <- ne_download(
        scale = 110,
        type = "land",
        category = "physical",
        returnclass = "sf")

Affichage de la couche d’habillage avec la fonction mf_map du package mapsf.

mf_map(land, border = NA, col = "#6eb1db")

ocean <- ne_download(
         scale = 110,
         type = "ocean",
         category = "physical",
         returnclass = "sf")
mf_map(ocean, border = NA, col = "#6eb1db")

Puis, nous créons une couche de graticules avec la fonction st_graticule du package sf.

graticule = st_graticule(
            crs = st_crs(4326),
            ndiscr = 100,
            lon = seq(-180, 180, by = 2),
            lat = seq(-90, 90, by = 1),
            margin = 0.01)
mf_map(graticule, col = "#6eb1db")

2.2 Projection orthographique

Pour donner un effet de rotondité et permettre une représentation en 2.5D, on opte pour une projection orthographique centré sur l’Afrique. Pour éviter tout problème dans l’opération de projection (bug, artefacts, etc.), nous définissons au préalable un rectangle nous servant à découper les différentes couches.

# Construction du rectangle
bb <- st_as_sfc(x = st_bbox(c(xmin = -50 , xmax = 70, ymin = 20, ymax = 80),
                crs = st_crs(4326)))

Puis, nous découpons toutes les couches d’habillage en fonction du rectangle préalablement crée. Pour que cette opération se passe bien, nous utilisons la fonction sf_use_s2 du package sf en paramétrant l’argument use_s2 en FALSE pour faire comme si les latitudes et longitudes étaient des coordonnées euclidiennes. Une fois les intersections effectuées, nous réutilisons cette fonction en paramétrant l’argument use_s2 en TRUE.

sf_use_s2(FALSE)

ocean <- st_intersection(ocean, bb)
ocean <-  st_segmentize(ocean, 100)
land <- st_intersection(land, bb)
land <-  st_segmentize(land, 100)
graticule <- st_intersection(graticule, bb)

sf_use_s2(TRUE)

Nous projetons ensuite toutes les couches géographiques crées dans une projection orthographique centrée sur l’Afrique (10° de latitude nord et 15° de longitude).

# Définition de la projection en format "proj-strings" (PROJ.4) 
ortho <- "+proj=ortho +lat_0=-10 +lon_0=15 +x_0=0 +y_0=0
          +ellps=WGS84 +units=m +no_defs"

# Reprojection des couches géographiques
ocean <- st_transform(ocean, ortho)
land <- st_transform(land, ortho)
graticule <-  st_transform(graticule, ortho)
nuts <- st_transform(nuts, ortho)

Affichage de l’ensemble des couches recadrées et projetées, en utilisant des fonctions du package mapsf.

par(mar = c(0, 0, 0, 0), mfrow = c(2, 2))

mf_map(land, col = "#6eb1db", border = NA)
mf_title("land", bg = "#6eb1db")

mf_map(ocean, col = "#6eb1db", border = NA)
mf_title("ocean", bg = "#6eb1db")

mf_map(graticule, col = "#6eb1db", lwd = 1)
mf_title("graticule", bg = "#6eb1db")

mf_map(nuts, col = "#6eb1db", border = "white", lwd = 0.2)
mf_title("nuts", bg = "#6eb1db")

2.3 Effet d’ombrage

On peut générer un effet d’ombrage en agrégeant les régions NUTS et en effectuant des déplacements successifs avec de la transparence. Ci-dessous, un exemple sur la France métropolitaine :

# Union de l'ensemble des entités de la couche géographique nuts
fr <- st_union(nuts[substr(nuts$id, 1, 2) == "FR", ])

# Paramétrage des marges de la fenêtre graphique
par(mar = c(0, 0, 0, 0))

# Affichage multiple de la couche, en appliquant à chaque fois un léger décalage
mf_map(fr + c(5000,-5000), col = "#827e6c40", border = NA)
mf_map(fr + c(10000,-10000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(fr + c(15000,-15000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(fr + c(20000,-20000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(fr + c(25000,-25000), col = "#827e6c40", border = NA, add = TRUE)
mf_map(fr, col = "#6eb1db", border = "white", lwd = 0.1, add = TRUE)


2.4 Fonction template()

A partir des différentes couches géographiques construites, recadrées et reprojetées, nous réalisons à présent un modèle de mise en page cartographique.

Pour cela, nous définissons précisément l’emprise de la carte dans le système de coordonnées de la projection. Les coordonnées étant en mètres, nous utilisons un facteur 100 000 (variable k) qui nous permet de manipuler des chiffres plus petits avec un niveau de précision satisfaisant. Nous utiliserons également ce facteur k pour positionner les différents éléments d’habillage de la carte.

# Création de la variable k
k <- 100000

# Coordonnées de l'emprise * k
extent <- c(-20, 42, 24.5, 63) * k

# Construction de l'emprise (objet sfc)
bb <- st_as_sfc(x= st_bbox(c(xmin = extent[1], xmax = extent[3], ymin = extent[2], ymax = extent[4]),
               crs = st_crs(nuts)))

On crée ensuite la fonction template() qui contient le modèle de mise en page souhaité. Ce modèle est construit à l’aide de fonctions mises à disposition par le package mapsf.

# Création de la fonction
template = function(file) {
  
  # Création d'un thème
  theme <- mf_theme(x = "default",
                    bg = "#f2efe6",
                    fg = "#f2efe6",
                    mar = c(0, 0, 0, 0),
                    tab = TRUE,
                    pos = "left",
                    inner = FALSE,
                    line = 2,
                    cex = 1.9,
                    font = 3)
  
  # Paramétrage de l'export de la carte en format png
  mf_export(bb,
            export = "png",
            width = 2000,
            filename = file,
            res = 150,
            theme = theme,
            expandBB = c(-.02, 0, 0.05, 0))

  # Affichage de la couche 'ocean'
  mf_map(ocean, col = "#9acbe3", border = "#9acbe3", lwd = 5, add = TRUE)
  
  # Affichage de la couche 'graticule'      
  mf_map(graticule, col = "#FFFFFF80", lwd = 1.5, lty = 3, add = TRUE)
  
  
  # Affichage d'un effet d'ombrage pour la couche 'nuts'         
  ue <- st_union(nuts)
  mf_map(ue + c(5000,-5000), col = "#827e6c40", border = NA, add = TRUE)
  mf_map(ue + c(10000,-10000), col = "#827e6c40", border = NA, add = TRUE)
  mf_map(ue + c(15000,-15000), col = "#827e6c40", border = NA, add = TRUE)
  mf_map(ue + c(20000,-20000), col = "#827e6c40", border = NA, add = TRUE)
  mf_map(ue + c(25000,-25000), col = "#827e6c40", border = NA, add = TRUE)

  # Affichage de la couche 'nuts'
  mf_map(nuts, col = "#dbccb6", border = "white", lwd = 0.3, add = TRUE)
  
  # Affichage d'un bandeau bleu transparent sous le titre
  rect(-22 * k, 
       41.3 * k, 
       28 * k, 
       41.3 * k + 250000, 
       border = NA, col = "#2369bd80")

  # Affichage du titre
  text(x = -21.5 * k,
       y = 42.4 * k,
       labels = "30 YEARS LATER, THE IRON CURTAIN IS STILL VISIBLE",
       cex = 2.14,
       pos = 4,
       font = 2,
       col = "#FFFFFF80")
  
  # Affichage des sources
  text(x = -21.75 * k,
       y = 44.25 * k,
       labels = "Map 100% designed in the R language by Nicolas Lambert, 2019. 
Code source available here: https://github.com/neocarto/ironcurtain). Data sources: Eurostat & Natural Earth, 2019",
       cex = 0.5,
       pos = 4,
       font = 1,
       col = "#3f4654")
  
  # Affichage d'une échelle
  mf_scale(size = 700,
           lwd = 0.6,
           cex = 0.5,
           col = "#3f4654",
           pos = c(19 * k, y = 44 * k))
}

Et voilà le résultat :-)

# Indiquez le nom du fichier png crée
# Utilisez dev.off() pour clôturer l'export et l'enregistrement du fichier
template("figures/fig1.png")
dev.off()

Le résultat ne s’affiche pas dans la fenêtre graphique. Il est directement enregistré en format png dans le répertoire souhaité.

3 Carte choroplèthe

Maintenant que le modèle de mise en page est reproductible avec la fonction template() précédemment créée, penchons-nous sur la représentation cartographique du PIB par habitant.

3.1 Choix des classes et des couleurs

Nous utilisons la fonction mf_get_breaks() pour discrétiser notre distribution statistique.

# Discrétisation par quantile
bks <- mf_get_breaks(x = nuts$GDPINH_2016,
                     nbreaks = 6,
                     breaks = "quantile")

# Vecteur de couleurs (1 par classe)
cols <- c("#50b160",
          "#98c17e",
          "#cce3c4",
          "#fbf5bd",
          "#fcc34f",
          "#e97d40")

3.2 Réalisation de la carte

Nous pouvons alors facilement cartographier le PIB par habitant en utilisant la fonction de mise en page template() puis la fonction mf_map() pour représenter la variable souhaitée.

# Utilisation du modèle de mise en page (+ export de la carte)
template("figures/fig2.png")

# Cartographie du PIB par habitant
mf_map(x = nuts,
       var = "GDPINH_2016",
       type = "choro",
       breaks = bks,
       pal = cols,
       lwd = 0.2,
       leg_pos = "n",
       add = TRUE)

# Ajout d'une légende
mf_legend(type = "choro",
          pos = c(11 * k, 59.05 * k),
          title = "",
          val = bks,
          val_cex = 0.4,
          pal = cols,
          fg = "#333333",
          cex = 0.85,
          border = "red",
          val_rnd = 0,
          no_data = FALSE,
          frame = FALSE)

# Ajout d'un titre de légende
text(10.5 * k,
     y = 59.1 * k,
     labels = "Gross Domestic Product",
     cex = 0.75,
     pos = 4,
     font = 2,
     col = "#404040")

# Ajout d'un sous-titre de légende
text(10.5 * k,
     y = 58.7 * k,
     labels = "(in € per inh. in 2016)",
     cex = 0.55,
     pos = 4,
     font = 1,
     col = "#404040")

# NE PAS OUBLIER !
dev.off()

4 Discontinuités

La première étape pour cartographier les discontinuités spatiales est de récupérer les frontières entre les régions NUTS. Pour cela, nous calculons une petite zone tampon (pour éliminer les éventuelles erreurs topologiques) et effectuons une auto-intersection des polygones de la couche ‘nuts’. Nous définissons les géométries de sortie en MULTILINESTRING.

# Auto-intersection de la couche NUTS (avec buffer de 5m)
nuts.borders  <- st_intersection(st_buffer(nuts, 5), st_buffer(nuts, 5)) 

# Transformation des géométries en 'MULTILINESTRING'
nuts.borders  <- st_cast(nuts.borders ,"MULTILINESTRING")

# Suppression des intersections entre les mêmes polygones
nuts.borders  <- nuts.borders [nuts.borders $id != nuts.borders $id.1, ] 

# Construction d'un identifiant unique pour chaque frontière
nuts.borders$id1 <- nuts.borders$id
nuts.borders$id2 <- nuts.borders$id.1
nuts.borders$id <-  paste0(nuts.borders$id1, "_", nuts.borders$id2)
rownames(nuts.borders) <- nuts.borders$id
nuts.borders  <- nuts.borders [,c("id","id1","id2","geometry")]

Puis, nous effectuons une double jointure pour relier à chaque frontière NUTS les données de PIB par habitant des régions limitrophes.

# Récupération des données de PIB par habitant, en supprimant la géométrie associée
vals <- st_set_geometry(x = nuts[, c("id","GDPINH_2016")], 
                        value = NULL)

# Double jointure pour récupérer les valeurs des deux entités limitrophes
nuts.borders <- merge (x = nuts.borders, y = vals, by.x = "id1", by.y = "id", all.x = T)
nuts.borders <- merge (x = nuts.borders, y = vals, by.x = "id2", by.y = "id", all.x = T)

Nous calculons pour chaque frontière une valeur de discontinuité. Ici, discontinuités relatives :

nuts.borders$disc <- nuts.borders$GDPINH_2016.x / nuts.borders$GDPINH_2016.y

Nous choisissons de conserver uniquement les plus fortes discontinuités (10 %). Cela revient à choisir comme seuil la valeur 0.95 car la table présente deux valeurs pour chaque frontière (les rapports A/B et B/A).

threshold <- 0.95
disc <- nuts.borders[nuts.borders$disc >= quantile(nuts.borders$disc,threshold),]

Affichage des discontinuités sélectionnées dans le modèle de mise en page :

template("figures/fig3.png")

mf_map(x = disc,
       col = "#d92e94",
       lwd = 3,
       add = TRUE)

dev.off()

On constate que les fortes discontinuités entre les régions européennes suivent très largement le tracé de l’ancien rideau de fer (si on fait abstraction de ancienne frontière RFA/RDA). C’est cela que nous souhaitons mettre en valeur par un procédé d’extrusion pour rappeler la symbolique du mur.

5 Extrusion

Pour extruder les lignes, nous procédons de la même façon que pour l’effet d’ombrage (cf. partie 1.6). Nous translatons les lignes plusieurs fois en ordonnée (y, vers le haut). Sur la projection orthographique utilisée, cela produit un effet 3D. Le nombre d’itérations et l’écart entre les lignes détermine la hauteur du mur. Exemple pour une frontière :

# On sélectionne une ligne (frontière) au hasard
line <- st_geometry(disc[5, ])

# nombre d'itérations
nb <- 15

# valeur de translation
delta <- 200

On effectue alors une boucle for qui construit 15 (‘nb’) lignes, décalées de ‘delta’ à chaque fois.

# Paramétrage des marges et de la couleur de fond
par(mar = c(0, 0, 0, 0), bg = "#f2efe6")

# Affichage de la frontière
mf_map(line, col = "#66666690", lwd = 0.5)

# 'nb' affichage de la frontière, décalée de 'delta' à chaque fois
for (j in 1:nb) {
  line <- line + c(0, delta)
  mf_map(line,
         col = "#66666690",
         lwd = 0.5 ,
         add = TRUE)
}

# Affichage de la dernière ligne calculée en rouge, avec une épaisseur de 1.2
mf_map(line,
       col = "#cf0e00",
       lwd = 1.2,
       add = TRUE)

Pour que l’effet 3D fonctionne bien, l’ordre d’affichage des discontinuités est important. Nous décomposons donc les lignes et les ordonnons en fonction de la valeur en ordonnée (y) de leur centroïde. Cela permet d’afficher les discontinuités du Nord au Sud. Ainsi, les lignes qui sont “devant” apparaissent devant.

# Transformation du type de géométrie
disc <- st_cast(disc,"LINESTRING")

# Calcul des coordonnées des centroïdes de chaque lignes
c <- as.data.frame(st_coordinates(st_centroid(disc)))

# Récupération de la valeur y de chaque centroïde
disc$Y <- c$Y

# Tri des discontinuités en fonction de la valeur de y 
disc <- disc[order(disc$Y, decreasing = TRUE), ]

Nous considérons que les frontières nationales renvoient à des disparités historiques plus dures. Ainsi, nous choisissons de traiter différemment les discontinuités entre deux régions d’un même pays et les discontinuités entre deux régions de deux pays différents. Les premières auront une “hauteur de mur” constante (8 itérations). Les secondes auront une “hauteur de mur” qui dépendra de la valeur des discontinuités (entre 30 et 70 itérations) et seront représentées en rouges.

Dans un premier temps, nous calculons une “hauteur de mur” proportionnelle à la valeur de discontinuité relative de chaque ligne. Nous rééchelonnons l’étendue de la distribution entre les valeurs 30 et 70. La plus faible (des plus fortes) discontinuité sera itérée 30 fois, la plus forte 70 fois.

Pour réaliser ce rééchelonnage, nous utilisons la fonction rescale du package scale.

# Rééchelonnage des valeurs de discontinuité, de 30 à 70
disc$height <- round(rescale(disc$disc, to=c(30,70)),0)

On affecte également par défaut la couleur rouge et la même épaisseur à toutes les lignes, en les stockant dans deux nouvelles variables (‘col’ et ‘thickness’).

disc$col <-"#cf0e00"
disc$thickness <- 1.2

Nous devons ensuite repérer les discontinuités infra-nationales afin de modifier les valeurs à représenter. Pour cela nous utilisons les deux premières lettres des codes NUTS associées à chaque ligne, puisqu’elles correspondent au code ISO du pays d’appartenance.

disc$c1 <- substr(disc$id1,1,2)
disc$c2 <- substr(disc$id2,1,2)

A l’aide d’une boucle for et d’un test if, nous pouvons détecter les frontières infra-nationales. Nous modifions alors les valeurs des variables ‘height’, ‘col’ et ‘thickness’ (qui seront utilisées pour la représentation des discontinuités) pour toutes ces lignes.

# Boucle qui parcours toutes les lignes
for (i in 1:length(disc$disc)){ 
  
  # Pays 1 == Pays 2 ?
  if (disc$c1[i]== disc$c2[i]) {
      disc$height[i] <- 8  
      disc$col[i] <-"#66666690"
      disc$thickness[i] <- 0.5
      
  }
}

Toutes les valeurs à représenter ont été calculées. Nous pouvons alors construire une fonction d’extrusion (représentation graphique) des lignes en fonction des valeurs calculées.

# valeur de translation
delta <- 2500  

# Création de la fonction extrude()
extrude <- function(id){

  line <- st_geometry(disc[id,])
  mf_map(line, col= "#66666690",lwd = 0.5 ,add= TRUE)
  nb <- as.numeric(disc[id,"height"])[1]
  
  for (j in 1:nb){
    line <- st_geometry(line) + c(0,delta)
    mf_map(st_geometry(line), col= "#66666690",lwd = 0.5 ,add= TRUE)
  }
  
  mf_map(line, col= disc$col[id],lwd = disc$thickness[id] ,add= TRUE)
  
}

Nous pouvons appliquer la fonction à toutes les lignes en utilisant une boucle for. Voici un exemple d’utilisation de la fonction extrude(), combiné avec l’utilisation de template() et l’ajout d’une légende pour la représentation des discontinuités.

template("figures/fig4.png")

# BOUCLE for pour appliquer la fonction extrude() à toutes les lignes
for (i in 1:length(disc$height))
{
  extrude(i)
}

# TEXTE de la légende pour les discontinuités
# Utilisation du facteur k pour un placement précis des éléments
d = 0.75 * k
# Titre légende
text(14.4*k - d, y = 57.4*k, "Discontinuities", 
     cex = 0.6, pos = 4, font = 2, col="#404040")     
# Intitulé classe 1
text(15.5*k - d, y = 56.6*k, "Between two regions of the same country", 
     cex = 0.4, pos = 4, font = 1, col="#404040")   
# Intitulé classe 2
text(15.5*k - d, y = 55.7*k, "Between two regions of two different countries",
     cex = 0.4, pos = 4, font = 1, col="#404040")   
# Description
text(15.5*k - d, y = 55.3*k,
     "(The height is proportional to the value of the dicontinuity)",
     cex = 0.4, pos = 4, font = 1, col="#404040") 
# NB
text(10.7*k, y = 54.4*k,
     "NB: only the 10% highest discontinuities are represented on the map.",
     cex = 0.4, pos = 4, font = 3, col="#404040")   


# SYMBOLE pour la légende
# Récupération d'un ligne infra-nationale presque horizontale
myline <- disc[disc$id == "TR21_BG341",]
# On duplique a ligne
myline2 <- myline

# Translation de la ligne pour la positionner dans la légende (Classe 1)
st_geometry(myline) <- st_geometry(myline) + c(4.2*k, 5*k)
# Translation de la ligne pour la positionner dans la légende (Classe 2) 
myline2 <- myline
st_geometry(myline2) <- st_geometry(myline2) + c(0, 1.5*k)

# Symbole de légende pour les discontinuités entre deux pays
plot(myline, col= "#66666690",lwd = 0.5 ,add= T)
# 40 itération de la ligne
for (i in 1:40){
    myline <- st_geometry(myline) + c(0,delta)
    plot(st_geometry(myline), col= "#66666690",lwd = 0.5 ,add= T)  
}
# Dernière ligne en rouge
plot(myline, col= "#cf0e00",lwd = 1.2 ,add= T)

# Symbole de légende pour les discontinuités infra-nationale
plot(myline2, col= "#66666690",lwd = 0.5 ,add= T)
# 8 itération de la ligne
for (i in 1:8){
  myline2 <- st_geometry(myline2) + c(0,delta)
  plot(st_geometry(myline2), col= "#66666690",lwd = 0.5 ,add= T)  
}
plot(myline2, col= "#66666690",lwd = 0.5 ,add= T)

# Ne pas oublier
dev.off()

6 Carte finale

Les données sont prêtes à être cartographiées. La variable a représenter a été discrétisée. Le modèle de mise en page et la représentation des discontinuités en 2,5D ont été encapsulés dans des fonctions. Nous avons donc tous les éléments pour générer la carte souhaitée :

# Modèle de mise page
template("figures/ironcurtain.png")

# Carte choroplèthe
mf_map(x = nuts,
       var = "GDPINH_2016",
       type = "choro",
       breaks = bks,
       pal = cols,
       lwd = 0.2,
       leg_pos = "n",
       add = TRUE)

# Légende carte choroplèthe
mf_legend(type = "choro",
          pos = c(11 * k, 59.05 * k),
          title = "",
          val = bks,
          val_cex = 0.4,
          pal = cols,
          fg = "#333333",
          cex = 0.85,
          border = "red",
          val_rnd = 0,
          no_data = FALSE,
          frame = FALSE)

# Titre légende carte choroplèthe
text(10.5 * k,
     y = 59.1 * k,
     labels = "Gross Domestic Product",
     cex = 0.75,
     pos = 4,
     font = 2,
     col = "#404040")

# Sous-titre légende carte choroplèthe
text(10.5 * k,
     y = 58.7 * k,
     labels = "(in € per inh. in 2016)",
     cex = 0.55,
     pos = 4,
     font = 1,
     col = "#404040")

# Construction discontinuités
for (i in 1:length(disc$height)){extrude(i)}

# Texte légende discontinuités
d = 0.75 * k
text(14.4*k - d, y = 57.4*k, "Discontinuities", 
     cex = 0.6, pos = 4, font = 2, col="#404040")     
text(15.5*k - d, y = 56.6*k, "Between two regions of the same country", 
     cex = 0.4, pos = 4, font = 1, col="#404040")   
text(15.5*k - d, y = 55.7*k, "Between two regions of two different countries",
     cex = 0.4, pos = 4, font = 1, col="#404040")   
text(15.5*k - d, y = 55.3*k,
     "(The height is proportional to the value of the dicontinuity)",
     cex = 0.4, pos = 4, font = 1, col="#404040") 
text(10.7*k, y = 54.4*k,
     "NB: only the 10% highest discontinuities are represented on the map.",
     cex = 0.4, pos = 4, font = 3, col="#404040")  

# Symbole légende discontinuités
myline <- disc[disc$id == "TR21_BG341",]
myline2 <- myline
st_geometry(myline) <- st_geometry(myline) + c(4.2*k, 5*k)
myline2 <- myline
st_geometry(myline2) <- st_geometry(myline2) + c(0, 1.5*k)

plot(myline, col= "#66666690",lwd = 0.5 ,add= T)
for (i in 1:40){
    myline <- st_geometry(myline) + c(0,delta)
    plot(st_geometry(myline), col= "#66666690",lwd = 0.5 ,add= T)  
}
plot(myline, col= "#cf0e00",lwd = 1.2 ,add= T)

plot(myline2, col= "#66666690",lwd = 0.5 ,add= T)
for (i in 1:8){
  myline2 <- st_geometry(myline2) + c(0,delta)
  plot(st_geometry(myline2), col= "#66666690",lwd = 0.5 ,add= T)  
}
plot(myline2, col= "#66666690",lwd = 0.5 ,add= T)

# Clôture graphique - enregistrement png
dev.off()


Et voilà. Même si cela nécessite quelques tâtonnements pour positionner les différents éléments, nous venons de faire la démonstration qu’il est donc possible de faire de la cartographie d’édition, stylisée et mise en page, uniquement avec R :-)

Bibliographie

GIRAUD, Timothée, 2021. mapsf R package [en ligne]. 2021. S.l. : s.n. Disponible à l'adresse : https://riatelab.github.io/mapsf/.
GIRAUD, Timothée et LAMBERT, Nicolas, 2016. cartography: Create and Integrate Maps in your R Workflow. In : JOSS [en ligne]. août 2016. Vol. 1, n° 4. DOI 10.21105/joss.00054. Disponible à l'adresse : http://dx.doi.org/10.21105/joss.00054.
GIRAUD, Timothée et LAMBERT, Nicolas, 2017. Reproducible Cartography. In : PETERSON, Michael (éd.), Advances in Cartography and GIScience. ICACI 2017. Lecture Notes in Geoinformation and Cartography. Cham, Switzerland : Springer. 2017. pp. 173‑183.
LAHTI, Leo, HUOVARI, Janne, KAINU, Markus et BIECEK, Przemyslaw, 2017. eurostat R package [en ligne]. 2017. S.l. : s.n. Disponible à l'adresse : https://journal.r-project.org/archive/2017/RJ-2017-019/index.html.
LAMBERT, Nicolas, 2017. La mise en scène cartographique. In : [en ligne]. 2017. [Consulté le 3 novembre 2017]. Disponible à l'adresse : https://neocarto.hypotheses.org/3239.
LAMBERT, Nicolas, 2019. Dessiner c’est coder ! In : [en ligne]. 2019. [Consulté le 8 novembre 2019]. Disponible à l'adresse : https://neocarto.hypotheses.org/6830.
LAMBERT, Nicolas et ZANIN, Christine, 2016. Manuel de cartographie: principes, méthodes, applications [en ligne]. S.l. : Armand Colin. Disponible à l'adresse : https://www.armand-colin.com/manuel-de-cartographie-principes-methodes-applications-9782200612856.
LAMBERT, Nicolas et ZANIN, Christine, 2019. Mad Maps. L’atlas qui va changer votre vision du Monde [en ligne]. S.l. : Armand Colin. Disponible à l'adresse : https://www.armand-colin.com/mad-maps-latlas-qui-va-changer-votre-vision-du-monde-9782200625825.
PEBESMA, Edzer, 2018. Simple Features for R: Standardized Support for Spatial Vector Data. In : The R Journal [en ligne]. 2018. Vol. 10, n° 1, pp. 439‑446. DOI 10.32614/RJ-2018-009. Disponible à l'adresse : https://doi.org/10.32614/RJ-2018-009.
SOUTH, Andy, 2017. rnaturalearth: World Map Data from Natural Earth [en ligne]. S.l. : s.n. Disponible à l'adresse : https://CRAN.R-project.org/package=rnaturalearth.
WICKHAM, Hadley et SEIDEL, Dana, 2020. scales: Scale Functions for Visualization [en ligne]. S.l. : s.n. Disponible à l'adresse : https://CRAN.R-project.org/package=scales.


Annexes

Info session

setting value
version R version 4.0.2 (2020-06-22)
os Ubuntu 18.04.5 LTS
system x86_64, linux-gnu
ui X11
language (EN)
collate fr_FR.UTF-8
ctype fr_FR.UTF-8
tz Europe/Paris
date 2021-11-04
package ondiskversion source
eurostat 3.7.5 CRAN (R 4.0.2)
mapsf 0.3.0 CRAN (R 4.0.2)
rnaturalearth 0.1.0 CRAN (R 4.0.2)
scales 1.1.1 CRAN (R 4.0.2)
sf 1.0.2 CRAN (R 4.0.2)

Citation

Lambert N (2021). “Le nouveau rideau de fer.” doi: 10.48645/a4ra-yr11, (URL: https://doi.org/10.48645/a4ra-yr11), <URL:, https://rzine.fr/publication_rzine/20191125_ironcurtain/>.

BibTex :

@Misc{,
  title = {Le nouveau rideau de fer},
  subtitle = {Un exemple de carte en 2.5D},
  author = {Nicolas Lambert},
  doi = {10.48645/a4ra-yr11},
  url = {https://rzine.fr/publication_rzine/20191125_ironcurtain/},
  keywords = {FOS: Other social sciences},
  language = {fr},
  publisher = {FR2007 CIST},
  year = {2021},
  copyright = {Creative Commons Attribution Share Alike 4.0 International},
}



licensebuttons cc